/*
	Communication Layer

	This module defines the Communications

	Use the function hooks to utilise all the advanved features

	This library should be hardware independant
*/
#include "globals.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include "layer_uart.h"
#include "layer_comm.h"
#include "layer_consult.h"
#include <util/crc16.h>

// ---------------------------------------------------------------------
// Macros
#ifndef COMM_UART
	#define COMM_UART					0
#endif
#ifndef COMM_RTS_INT				
	#define COMM_RTS_INT				eIntNone
#endif

#define COMM_RX_BUFFER_SIZE			0x0420
#define COMM_TX_BUFFER_SIZE			0x0100

#define COMM_HDR_1					0xAA
#define COMM_HDR_2					0xA5
#define COMM_FTR_1					0x55
#define COMM_FTR_2					0x5A
#if SPEED == 16
#define COMM_BAUD				500000
#elif SPEED == 12
#define COMM_BAUD				375000
#elif SPEED == 8
#define COMM_BAUD				250000
#endif

#define uart_enable_tx(bEnable)				uart_enable_tx_0 (bEnable)
#define uart_enable_rx(bEnable)				uart_enable_rx_0 (bEnable)
#define uart_send_byte_unbuffered(data)		uart_send_byte_unbuffered_0 (data)
#define uart_get_byte()						uart_get_byte_0 ()

// ---------------------------------------------------------------------
// Typedefs
typedef struct _typCommVars {
//	_comm_handle	comm_handle;	/* Communication Hanlder */
	_comm_resp		comm_resp;
	uint16_t		nLength;		/* Length of the new comm frame */
	uint16_t		nAdded;			/* Number of bytes added */
	uint16_t		nCrc;			/* CRC Accumulator */
} _typCommVars;

// ---------------------------------------------------------------------
// Static Varialbes
static _typCommVars		typComVars;
static unsigned char	comm_tx_data[COMM_TX_BUFFER_SIZE];	//Ring Buffer
static unsigned char	comm_rx_data[COMM_RX_BUFFER_SIZE];	//Always from zero

// ---------------------------------------------------------------------
// Private Functions
void comm_recieve		(unsigned char c, u16 nLength);

// ---------------------------------------------------------------------
// Functions Bodies
void	comm_init	(_comm_resp comm_handle)
{
	uart_init(COMM_UART, COMM_BAUD, &comm_recieve,
			comm_tx_data, COMM_TX_BUFFER_SIZE,
			comm_rx_data, COMM_RX_BUFFER_SIZE,
			COMM_RTS_INT);

	typComVars.nLength = 0;
	typComVars.nAdded = 0;
//	typComVars.comm_handle = comm_handle;
	typComVars.comm_resp = comm_handle;

#ifdef DDR_CTS0
	ssbi(DDR_CTS0);
	ccbi(PORT_CTS0);
#endif
}

// Sending functions
uint8_t	comm_new	(_eMsgAdr eMsgTo, _eMsgAdr eMsgFrom, uint16_t nLength)
{
	unsigned char hdr[] = {COMM_HDR_1,COMM_HDR_2,eMsgTo,eMsgFrom,nLength>>8,nLength&0xFF};

	// Initialise the varialbes
	typComVars.nAdded = 0;
	typComVars.nLength = nLength;
	typComVars.nCrc = 0;

	// Start the sending of the buffer
	uart_send_buffer( COMM_UART, hdr, 6);

	return TRUE;
}

uint8_t	comm_append	(unsigned char* buffer, unsigned short nLength)
{
	// Disable interrupts
	cli();
	// Send the buffer
	uart_send_buffer( COMM_UART, buffer, nLength );

	// Increment numbers of bytes sent
	typComVars.nAdded += nLength;

	// Calculate the CRC
	do
	{
		typComVars.nCrc = _crc16_update(typComVars.nCrc, *buffer++);
	} while (--nLength);

	// Send the footer once complete
	if (typComVars.nAdded >= typComVars.nLength)
	{
		unsigned char footer[] = {typComVars.nCrc>>8,typComVars.nCrc,COMM_FTR_1,COMM_FTR_2 };

		// Start the sending of the buffer
		uart_send_buffer( COMM_UART, footer, 4);
	}

	// Enable interrupts
	//sei();
	
	return TRUE;
}

uint8_t comm_send	(void)
{
	if (typComVars.nAdded < typComVars.nLength)
	{
		comm_response(eCmdFwFailed);
	}

	typComVars.nAdded = 0;
	typComVars.nLength = 0;
	typComVars.nCrc = 0;

	return TRUE;
}

void comm_response(uint8_t response)
{
	unsigned char resp[] = {ID_LAST_COMMAND_STATUS,response};

	comm_new	(eMsgPc, eMsgAtmel, 2);
	comm_append	(resp, 2);
	comm_send	();

	sei();
}

void comm_recieve		(unsigned char c, u16 nLength)
{
	switch (nLength)
	{
	case 1:
		if (c != COMM_HDR_1)
		{
			uart_flush_rx_buffer(COMM_UART);
			return;
		}
		else
		{
			//consult_enable(0);
			uart_bytes_rx(COMM_UART,2);
			outb(TCNT2, 0x00);	/* ToDo: use a proper call to something here */
			return;
		}
		break;

	case 2:

//		uart_send_buffer(COMM_UART,comm_rx_data,2);

		if (c != COMM_HDR_2)
		{
			uart_flush_rx_buffer(COMM_UART);
			uart_bytes_rx(COMM_UART,1);
			return;
		}
		else
		{
			unsigned short			size	= 0;
			unsigned short			crc;
			register short			read;
			register unsigned short loop	= 4;
			register unsigned char	*pWrite = &comm_rx_data[2];

			uart_enable_rx(0);

			do
			{
				if ((read = uart_get_byte()) != -1)
				{
					*pWrite++ = read;
				}
				else
				{
					comm_response(eCmdRespIncomplete);
					uart_flush_rx_buffer(COMM_UART);
					uart_bytes_rx(COMM_UART,1);
					uart_enable_rx(1);
					return;
				}
			} while (--loop);

			// Remaining bytes
			loop = (((uint16_t)comm_rx_data[4] << 8) | comm_rx_data[5]) + 4;
			
			// Store final size
			size = loop + 6;

			if (loop > COMM_RX_BUFFER_SIZE)
			{
				comm_response(eCmdRespTooBig);
				uart_flush_rx_buffer(COMM_UART);
				uart_bytes_rx(COMM_UART,1);
				uart_enable_rx(1);
				return;
			}

			do
			{
				if ((read = uart_get_byte()) != -1)
				{
					*pWrite++ = read;
				}
				else
				{
					comm_response(eCmdRespIncomplete);
					uart_flush_rx_buffer(COMM_UART);
					uart_bytes_rx(COMM_UART,1);
					uart_enable_rx(1);
					return;
				}
			} while (--loop);

			// Check Heads
			if (comm_rx_data[0] != 0xAA
			 || comm_rx_data[1] != 0xA5
			 || comm_rx_data[size-2] != 0x55
			 || comm_rx_data[size-1] != 0x5A)
			{
				comm_response(eCmdRespBadHeads);
			}
			else
			{

				//uart_send_buffer(0, comm_rx_data, size);
				//uart_flush_rx_buffer(COMM_UART);
				//uart_bytes_rx(COMM_UART,1);
				//return;
#ifndef NO_CRC
				// Calculate CRC
				crc = 0;
				for(loop=6;loop<size-4;loop++)
				{	
					crc = _crc16_update(crc, comm_rx_data[loop]);
				}
				// Check CRC
				if (crc != ((uint16_t)comm_rx_data[size-4] << 8 | comm_rx_data[size-3]))
				{
					comm_response(eCmdRespCrcFailed);
				}
				else if (typComVars.comm_resp)
				{
#endif
					//comm_response(eCmdRespUnknownSender);
					// HANDLE THE COMMUNICATIONS HERE
					typComVars.comm_resp(comm_rx_data, size-10);
					
					//comm_usb_handler(comm_rx_data, size-10);
					//comm_response(eCmdRespNext);
#ifndef NO_CRC
				}
#endif
			}
		}

		uart_enable_rx(1);
		uart_flush_rx_buffer(COMM_UART);
		uart_bytes_rx(COMM_UART,1);
		break;

	default:
		uart_flush_rx_buffer(COMM_UART);
		comm_response(eCmdRespIncomplete);
		break;
	}
}

